using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Generic;
using System.Diagnostics;
using System.Web;

namespace WozLib.Net
{
  class HttpProcessor
  {

    private Socket _socket;
    
    private StreamReader _streamReader;
    private StreamWriter _streamWriter;
    private NetworkStream _networkStream;
    private String _method;
    private String _address;
    private String _protocol;
    private Hashtable _HashTable;

    public HttpProcessor(Socket socket)
    {
      this._socket = socket;
      _HashTable = new Hashtable();
    }

    public void process()
    {
      _networkStream = new NetworkStream(_socket);
      _streamReader = new StreamReader(_networkStream);
      _streamWriter = new StreamWriter(_networkStream);
      parseRequest();
      readHeaders();
      writeURL();
      _socket.Shutdown(SocketShutdown.Both);
      _networkStream.Close();
    }

    public void parseRequest()
    {
      String request = _streamReader.ReadLine();
      string[] tokens = request.Split(new char[] { ' ' });
      _method = tokens[0];
      _address = tokens[1];
      _protocol = tokens[2];
    }

    public void readHeaders()
    {
      String line;
      while ((line = _streamReader.ReadLine()) != null && line != "")
      {
        string[] tokens = line.Split(new char[] { ':' });
        String name = tokens[0];
        String value = "";
        for (int i = 1; i < tokens.Length; i++)
        {
          value += tokens[i];
          if (i < tokens.Length - 1) tokens[i] += ":";
        }
        _HashTable[name] = value;
      }
    }

    public void writeURL()
    {
      try
      {

        // go try to see if that image is in our registry
        // if it is, serve it up
        string filename = HttpUtility.UrlDecode(_address.Substring(1));

        filename = filename.Replace("/", "\\");

        // the && false has this disabled right now because
        // file registration is not implemented completley on
        // both the client and server.
        if (!HttpFileServer.IsFileRegistered(filename) && false)
        {
          throw new FileNotFoundException(filename);
        }

        try
        {
          // total hack
          writeSuccess(filename,File.ReadAllBytes(filename).Length);
          _streamWriter.Flush();
          _socket.SendFile(filename);
        }
        catch (Exception ex)
        {
          Debug.WriteLine(ex.ToString());
        }
      }
      catch (FileNotFoundException)
      {
        writeFailure();
        _streamWriter.WriteLine("File not found: " + _address);
      }
    }

    public void writeSuccess(string filename, int length)
    {
      _streamWriter.WriteLine("HTTP/1.1 200 OK");
      _streamWriter.WriteLine("Connection: close");
      _streamWriter.WriteLine();
    }

    public void writeFailure()
    {
      _streamWriter.WriteLine("HTTP/1.1 404 File not found");
      _streamWriter.WriteLine("Connection: close");
      _streamWriter.WriteLine();
    }
  }

  public class HttpFileServer
  {

    private static List<string> _RegisteredFiles = new List<string>();

    public static bool RegisterFile(string fileUrl)
    {
      if (_RegisteredFiles.Contains(fileUrl))
      {
        return false;
      }
      else
      {
        _RegisteredFiles.Add(fileUrl);
        return true;
      }
    }

    public static bool IsFileRegistered(string imageUrl)
    {
      return _RegisteredFiles.Contains(imageUrl);
    }

    protected int port;

    public HttpFileServer()
      : this(80)
    {
    }

    public HttpFileServer(int port)
    {
      this.port = port;
    }

    Socket _listener;
    bool _isServerRunning = true;
    
    List<HttpProcessor> _httpProcessors = new List<HttpProcessor>();

    public void Listen()
    {
      try {
      _listener = new Socket(0, SocketType.Stream, ProtocolType.Tcp);
      //_TcpListener = new TcpListener(IPAddress.Any, port);

      //IPAddress ipaddress = new IPAddress("127.0.0.1");
      IPAddress ipaddress = IPAddress.Any;
      IPEndPoint endpoint = new IPEndPoint(ipaddress, port);
      _listener.Bind(endpoint);
      _listener.Blocking = true;
      _listener.Listen(-1);
      //_TcpListener.Start();
      while (_isServerRunning)
      {
        try
        {
          Socket s = _listener.Accept();

          HttpProcessor processor = new HttpProcessor(s);
          _httpProcessors.Add(processor);
          Thread thread = new Thread(new ThreadStart(processor.process));
          thread.Start();
        }
        catch (Exception ex)
        {
          Debug.WriteLine(ex.ToString());
        }
      }
    }
    catch (Exception ex)
    {
      Debug.WriteLine(ex.ToString());
    }
    }

    public static string GetRemoteFileUrl(string fileUrl)
    {
      string protocol = "HTTP://";
      string hostname = WozServer.GetHostName();
      string portname = ":" + WozServer.HTTP_FILE_SERVER_PORT.ToString() + "/";
      string toReturn = protocol + hostname + portname + fileUrl;

      return toReturn;
    }

    public void Stop()
    {
      try
      {
        _listener.Close();
      }
      catch (Exception ex)
      {
        Debug.WriteLine(ex.ToString());
      }
      _isServerRunning = false;
    }
  }
}
